using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
using System.Workflow.ComponentModel;
using System.Workflow.ComponentModel.Design;
using System.Diagnostics;
using System.Reflection;
using OpenComposite.Workflow.Properties;
using System.Data;
using System.Workflow.Activities;
using System.Workflow.ComponentModel.Compiler;

namespace OpenComposite.Workflow
{
	[ActivityValidator(typeof(HorizontalWorkflowActivityValidator))]
	[Designer(typeof(HorizontalWorkflowDesigner), typeof(IRootDesigner))]
	public class HorizontalWorkflowActivity : HorizontalSequenceActivity
	{
		//public static DependencyProperty RunTypeProperty = DependencyProperty.Register("RunType", typeof(RunType), typeof(HorizontalWorkflowActivity));

		//[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		//[Browsable(false)]
		//public RunType RunType
		//{
		//    get { return (RunType)GetValue(RunTypeProperty); }
		//    set { SetValue(RunTypeProperty, value); }
		//}

		//public static DependencyProperty StartTypeProperty = DependencyProperty.Register("StartType", typeof(EventType), typeof(HorizontalWorkflowActivity));

		//[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		//public EventType StartType
		//{
		//    get { return (EventType)GetValue(StartTypeProperty); }
		//    set { SetValue(StartTypeProperty, value); }
		//}

		//public static DependencyProperty EndTypeProperty = DependencyProperty.Register("EndType", typeof(EventType), typeof(HorizontalWorkflowActivity));

		//[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
		//public EventType EndType
		//{
		//    get { return (EventType)GetValue(EndTypeProperty); }
		//    set { SetValue(EndTypeProperty, value); }
		//}

		public static DependencyProperty WhileConnectorsProperty = DependencyProperty.Register("WhileConnectors", typeof(FreeConnectorCollection), typeof(HorizontalWorkflowActivity));

		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public FreeConnectorCollection WhileConnectors
		{
			get
			{
				if ( GetValue(WhileConnectorsProperty) == null )
					SetValue(WhileConnectorsProperty, new FreeConnectorCollection());

				return (FreeConnectorCollection)GetValue(WhileConnectorsProperty);
			}
			set { SetValue(WhileConnectorsProperty, value); }
		}

		public static DependencyProperty NewLineConnectorsProperty = DependencyProperty.Register("NewLineConnectors", typeof(FreeConnectorCollection), typeof(HorizontalWorkflowActivity));

		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public FreeConnectorCollection NewLineConnectors
		{
			get
			{
				if ( GetValue(NewLineConnectorsProperty) == null )
					SetValue(NewLineConnectorsProperty, new FreeConnectorCollection());

				return (FreeConnectorCollection)GetValue(NewLineConnectorsProperty);
			}
			set { SetValue(NewLineConnectorsProperty, value); }
		}

		//public static DependencyProperty DocumentFlowsProperty = DependencyProperty.Register("DocumentFlows", typeof(DocumentFlowConnectorCollection), typeof(HorizontalWorkflowActivity));

		//[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		//public DocumentFlowConnectorCollection DocumentFlows
		//{
		//    get
		//    {
		//        if ( GetValue(DocumentFlowsProperty) == null )
		//            SetValue(DocumentFlowsProperty, new DocumentFlowConnectorCollection());

		//        return (DocumentFlowConnectorCollection)GetValue(DocumentFlowsProperty);
		//    }
		//    set { SetValue(DocumentFlowsProperty, value); }
		//}

		protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext)
		{
			return base.Execute(executionContext);
		}
	}

	[ActivityDesignerTheme(typeof(HorizontalWorkflowTheme))]
	public sealed class HorizontalWorkflowDesigner : HorizontalSequenceDesigner
	{
		#region Private Members

		#region Constants
		private const int terminatorSize = 20;
		private const int _connectorLength = 30;
		private const int connWidth = 5;
		#endregion

		#region Fields
		private IDesignerHost designerHost;
		private WorkflowView workflowView;
		private ISelectionService selectionService;

		private bool _isAdjustingConnector = false;
		private SelectionType _selectionType = SelectionType.None;
		//private bool _isDragging = false;
		private Point _dragPosition = new Point();
		private List<Point> _innerConnections = new List<Point>();

		private LinearGradientBrush _headerBrush;
		private int width = 40;
		private Font _headerFont = new Font(FontFamily.GenericSansSerif, 12.0F, FontStyle.Bold);
		private Pen _outLinePen = new Pen(new HatchBrush(HatchStyle.Percent20, Color.Gray, Color.Transparent), 7);

		private List<Point> points = new List<Point>();
		private int selectedPath = -1;
		private List<GraphicsPath> whileConnectors = new List<GraphicsPath>();
		private List<GraphicsPath> newLineConnectors = new List<GraphicsPath>();
		private ReadOnlyCollection<Point> terminators;
		private List<DesignerView> _views = null;

		private GraphicsPath _documentPath = null;

		private List<Activity> _branchList = new List<Activity>();
		private ActivityDesignerVerbCollection verbs = null;

		#endregion

		private void drawBPMNElement(Graphics graphics, ActivityDesignerTheme theme, Rectangle destingation, EventType type)
		{
			Rectangle bounds = destingation;
			switch ( type ) {
				case EventType.None:
					break;
				case EventType.Message:
					bounds.Inflate(-3, -4);
					graphics.DrawRectangle(theme.ForegroundPen, bounds);
					graphics.DrawPolygon(theme.ForegroundPen,
						new Point[]{
							new Point(bounds.Left, bounds.Top),
							new Point(bounds.Left + bounds.Width / 2, bounds.Y + bounds.Height / 2),
							new Point(bounds.Right, bounds.Top)
						});
					break;
				case EventType.Timer:
					bounds.Inflate(-3, -3);
					graphics.DrawEllipse(theme.ForegroundPen, bounds);
					graphics.DrawLine(theme.ForegroundPen,
						WorkflowHelpers.GetCenter(bounds),
						new Point(WorkflowHelpers.GetCenter(bounds).X + 4, WorkflowHelpers.GetCenter(bounds).Y - 4));
					graphics.DrawLine(theme.ForegroundPen,
						WorkflowHelpers.GetCenter(bounds),
						new Point(WorkflowHelpers.GetCenter(bounds).X + 3, WorkflowHelpers.GetCenter(bounds).Y + 3));
					break;
				case EventType.Rule:
					break;
				case EventType.Exception:
					bounds.Inflate(-7, -2);
					GraphicsPath path = new GraphicsPath();
					path.AddLine(bounds.Left, bounds.Top, bounds.Left, bounds.Bottom);
					path.AddLine(bounds.Left, bounds.Bottom, bounds.Left + 1, bounds.Top - 1);
					path.CloseFigure();
					path.AddLine(bounds.Right, bounds.Bottom, bounds.Right, bounds.Top);
					path.AddLine(bounds.Right, bounds.Top, bounds.Right - 1, bounds.Bottom + 1);
					path.CloseFigure();
					path.AddLine(bounds.Right - 1, bounds.Bottom, bounds.Left + 1, bounds.Top);
					graphics.DrawPath(theme.ForegroundPen, path);
					//graphics.FillPath(theme.ForegroundBrush, path);
					break;
				case EventType.Cancellation:
					break;
				case EventType.Compensation:
					bounds.Inflate(-2, -5);
					Point center = WorkflowHelpers.GetCenter(bounds);
					graphics.FillPolygon(theme.ForegroundBrush,
						new Point[]{
							new Point(bounds.Left, center.Y),
							new Point(center.X, bounds.Top),
							new Point(center.X, bounds.Bottom)
						});
					graphics.FillPolygon(theme.ForegroundBrush,
						new Point[]{
							new Point(center.X, center.Y),
							new Point(bounds.Right, bounds.Top),
							new Point(bounds.Right, bounds.Bottom)
						});
					break;
				case EventType.Link:
					break;
				case EventType.Multiple:
					break;
				case EventType.Termination:
					break;
				default:
					break;
			}
		}

		private void workflowView_MouseDown(object sender, MouseEventArgs e)
		{
			if ( selectionService == null )
				selectionService = (ISelectionService)GetService(typeof(ISelectionService));
			selectedPath = -1;
			Invalidate();

			if ( e.Clicks == 1 && e.Button == MouseButtons.Left ) {
				Rectangle rc = new Rectangle();
				//List<Activity> la = new List<Activity>(1);
				//la.Add(this.Activity);
				for ( int i = 0; i < whileConnectors.Count; i++ ) {
					if ( whileConnectors[i].IsOutlineVisible(PointToLogical(workflowView.PointToScreen(e.Location)), _outLinePen) ) {
						selectedPath = i;
						_selectionType = SelectionType.Jump;
						rc = new Rectangle((int)whileConnectors[i].GetBounds().X,
								(int)whileConnectors[i].GetBounds().Y,
								(int)whileConnectors[i].GetBounds().Width,
								(int)whileConnectors[i].GetBounds().Height);
						rc.Inflate(5, 5);
						Invalidate(rc);
						break;
					} else {
						if ( selectedPath >= 0 ) {
							selectedPath = -1;
							_selectionType = SelectionType.None;
							Invalidate();
						}
					}
				}
				if ( selectedPath != -1 ) return;

				for ( int i = 0; i < newLineConnectors.Count; i++ ) {
					if ( newLineConnectors[i].IsOutlineVisible(PointToLogical(workflowView.PointToScreen(e.Location)), _outLinePen) ) {
						selectedPath = i;
						_selectionType = SelectionType.NewLine;
						rc = new Rectangle((int)newLineConnectors[i].GetBounds().X,
								(int)newLineConnectors[i].GetBounds().Y,
								(int)newLineConnectors[i].GetBounds().Width,
								(int)newLineConnectors[i].GetBounds().Height);
						rc.Inflate(5, 5);
						Invalidate(rc);
						break;
					} else {
						if ( selectedPath >= 0 ) {
							selectedPath = -1;
							_selectionType = SelectionType.None;
							Invalidate();
						}
					}
				}
				//if (selectedPath >= 0)
				//    selectionService.SetSelectedComponents(la);
			} else {
				selectedPath = -1;
			}
		}

		void workflowView_MouseMove(object sender, MouseEventArgs e)
		{
			if ( selectedPath >= 0 && e.Button == MouseButtons.Left ) {
				// performance?
				List<Activity> la = new List<Activity>(1);
				la.Add(this.Activity);
				selectionService.SetSelectedComponents(la);
				if ( _selectionType == SelectionType.Jump ) {
					if ( _isAdjustingConnector ||
						( PointToScreen(Point.Round(whileConnectors[selectedPath].PathPoints[1])).Y < workflowView.PointToScreen(e.Location).Y + 3 &&
						 PointToScreen(Point.Round(whileConnectors[selectedPath].PathPoints[1])).Y > workflowView.PointToScreen(e.Location).Y - 3 ) ) {
                        
						_isAdjustingConnector = true;

						int newLevel = PointToLogical(workflowView.PointToScreen(e.Location)).Y/* - TitleHeight - 10*/;
						if ( newLevel > 75 && newLevel < this.Bounds.Height+20 ) {
							Workflow.WhileConnectors[selectedPath].Level = newLevel;

							RectangleF revalRc = whileConnectors[selectedPath].GetBounds();
                            revalRc.Inflate(50, 90);
                            revalRc.Width += 80;
							Invalidate(new Rectangle(new Point((int)revalRc.X, (int)revalRc.Y), revalRc.Size.ToSize()));
						} else {
							if ( newLevel < 75 ) {
								Workflow.WhileConnectors[selectedPath].Level = 75;
							} else if ( newLevel > this.Bounds.Height + 20 ) {
								Workflow.WhileConnectors[selectedPath].Level = this.Bounds.Height + 20;
							}

							RectangleF revalRc = whileConnectors[selectedPath].GetBounds();
							revalRc.Inflate(50, 70);
							Invalidate(new Rectangle(new Point((int)revalRc.X, (int)revalRc.Y), revalRc.Size.ToSize()));
						}
					}
				} else if ( _selectionType == SelectionType.NewLine ) {
					if ( _isAdjustingConnector ||
						( PointToScreen(Point.Round(newLineConnectors[selectedPath].PathPoints[1])).Y < workflowView.PointToScreen(e.Location).Y + 3 &&
						 PointToScreen(Point.Round(newLineConnectors[selectedPath].PathPoints[1])).Y > workflowView.PointToScreen(e.Location).Y - 3 ) ) {

						_isAdjustingConnector = true;


						Workflow.NewLineConnectors[selectedPath].Level = PointToLogical(workflowView.PointToScreen(e.Location)).Y/* - TitleHeight - 10*/;

						RectangleF revalRc = newLineConnectors[selectedPath].GetBounds();
						revalRc.Inflate(5, 70);
						Invalidate(new Rectangle(new Point((int)revalRc.X, (int)revalRc.Y), revalRc.Size.ToSize()));
					}
				}
			}
		}

		void workflowView_MouseUp(object sender, MouseEventArgs e)
		{
			workflowView.Cursor = Cursors.Default;
			_isAdjustingConnector = false;
			//_selectionType = SelectionType.None;
		}

		private void PaintDropIndicators(ActivityDesignerPaintEventArgs e)
		{
			Rectangle[] targets = this.GetDropTargets(Bounds.Location/*new Point(Location.X + 5, Location.Y + 5)*/);
			points.Clear();
			points.AddRange(GetInnerConnections(DesignerEdges.Right));
			for ( int i = 0; i < targets.Length; i++ ) {
				if ( !targets[i].Contains(_dragPosition) ) {
					if ( e.Graphics.IsVisible(points[i]) ) {
						int x = WorkflowHelpers.GetCenter(targets[i]).X - ( Resources.smalladd.Width / 2 );
						int y = points[i].Y - ( Resources.smalladd.Height / 2 );
						e.Graphics.DrawImageUnscaled(Resources.smalladd, new Point(x, y));
					}
				}
			}
		}

		private void GetWhileConnectors()
		{
			whileConnectors.Clear();
			List<FreeConnector> tmp = new List<FreeConnector>(Workflow.WhileConnectors);
			foreach ( FreeConnector conn in tmp ) {

				Activity source = Workflow.GetActivityByName(conn.Start);
				Activity target = Workflow.GetActivityByName(conn.End);
				if ( source == null || target == null ) {
					selectedPath = -1;
					Workflow.WhileConnectors.Remove(conn);
					continue;
				}
				ActivityDesigner start = null;
				ActivityDesigner end = null;
				try {
					start = WorkflowHelpers.GetTopMostExpanded((ActivityDesigner)designerHost.GetDesigner(source));
					end = WorkflowHelpers.GetTopMostExpanded((ActivityDesigner)designerHost.GetDesigner(target));
				} catch { continue; }
				if ( start == null || end == null ) continue;
				int top;
				int bottom;
				if ( conn.Level == 0 ) {
					top = start.ParentDesigner.Bounds.Top;
					bottom = start.ParentDesigner.Bounds.Bottom;
					if ( start.ParentDesigner is HorizontalWorkflowDesigner )
						top += TitleHeight + 10;
				} else {
					top = conn.Level;
					bottom = conn.Level;
				}

				Point startCenter = new Point(WorkflowHelpers.GetCenter(start.Bounds).X, WorkflowHelpers.GetCenter(start.Bounds).Y + TitleHeight);
				Point endCenter = new Point(WorkflowHelpers.GetCenter(end.Bounds).X, WorkflowHelpers.GetCenter(end.Bounds).Y + TitleHeight);

				int centerY = WorkflowHelpers.GetCenter(Bounds).Y + TitleHeight;

                bool arrangeTop = ( startCenter.Y >= endCenter.Y );
				if ( arrangeTop ) conn.Level = top;
				else conn.Level = bottom;

				//// go from top of whilesplit?
				// startTop = arrangeTop;
				//// enter at top of targetCapability?
				// endTop = ( ( !startTop && startCenter.Y > endCenter.Y ) ||
				//               ( startTop && startCenter.Y < endCenter.Y ) );

				arrangeTop = conn.Level < startCenter.Y;
				//bool startTop = conn.Level <= startCenter.Y;
                //bool endTop = conn.Level < startCenter.Y && startCenter.Y <= endCenter.Y;
                bool endTop = conn.Level < endCenter.Y;

				if ( arrangeTop ) {
					GraphicsPath path = new GraphicsPath(FillMode.Winding);
					path.AddLine(
						new Point(startCenter.X, start.Bounds.Top),
						new Point(startCenter.X, top));
					path.AddLine(
						new Point(startCenter.X, top),
						new Point(endCenter.X, top));
					path.AddLine(
						new Point(endCenter.X, top),
						new Point(endCenter.X, endTop ? end.Bounds.Top : end.Bounds.Bottom));

					whileConnectors.Add(path);
				} else {
					GraphicsPath path = new GraphicsPath(FillMode.Winding);
					path.AddLine(
						new Point(startCenter.X, start.Bounds.Bottom),
						new Point(startCenter.X, bottom));
					path.AddLine(
						new Point(startCenter.X, bottom),
						new Point(endCenter.X, bottom));
					path.AddLine(
						new Point(endCenter.X, bottom),
						new Point(endCenter.X, endTop ? end.Bounds.Top : end.Bounds.Bottom));

					whileConnectors.Add(path);
				}
			}
		}

		private void GetNewLineConnectors()
		{
			newLineConnectors.Clear();
			if ( Workflow.Activities.Count > 1 ) {
				if ( Workflow.NewLineConnectors.Count == 0 ) {
					for ( int i = 0; i < Workflow.EnabledActivities.Count - 1; i++ ) {
						Workflow.NewLineConnectors.Add(
							new FreeConnector(Workflow.Activities[i].Name, Workflow.Activities[i + 1].Name));
					}
				}
			}
			//Workflow.NewLineConnectors.RemoveRange(Workflow.Activities.Count - 1, Workflow.NewLineConnectors.Count - (Workflow.Activities.Count - 1));
			List<FreeConnector> tmp = new List<FreeConnector>(Workflow.NewLineConnectors);

			foreach ( FreeConnector conn in tmp ) {
				Activity source = Workflow.GetActivityByName(conn.Start);
				Activity target = Workflow.GetActivityByName(conn.End);
				if ( target == null ) {
					selectedPath = -1;
					int iAct = Workflow.Activities.IndexOf(source) + 1;
					if ( iAct >= Workflow.Activities.Count ) {
						Workflow.NewLineConnectors.Remove(conn);
					} else {
						conn.End = Workflow.Activities[iAct].Name;
						conn.Level = 0;
						for ( int i = Workflow.NewLineConnectors.IndexOf(conn) + 1; i < Workflow.NewLineConnectors.Count; i++ ) {
							Workflow.NewLineConnectors[i].Level = 0;
						}
					}
					continue;
				} if ( source == null ) {
					selectedPath = -1;
					Workflow.NewLineConnectors.Remove(conn);
					continue;
				}
				ActivityDesigner start = WorkflowHelpers.GetTopMostExpanded((ActivityDesigner)designerHost.GetDesigner(source));
				ActivityDesigner end = WorkflowHelpers.GetTopMostExpanded((ActivityDesigner)designerHost.GetDesigner(target));

				if ( conn.Level == 0 ) {
					conn.Level = start.Bounds.Bottom + 25;
				} else if ( conn.Level < start.Bounds.Bottom ) {
					conn.Level = start.Bounds.Bottom;
				} else if ( conn.Level > end.Bounds.Top ) {
					conn.Level = end.Bounds.Top;
				}

				GraphicsPath path = new GraphicsPath();

				//Point[] pts = new Point[]{
				//    new Point(start.Bounds.Right, WorkflowHelpers.GetCenter(start.Bounds).Y),
				//    new Point(start.Bounds.Right, conn.Level),
				//    new Point(end.Bounds.Left, conn.Level),
				//    new Point(end.Bounds.Left, WorkflowHelpers.GetCenter(end.Bounds).Y)
				//};
				path.AddLine(
					new Point(start.Bounds.Right, WorkflowHelpers.GetCenter(start.Bounds).Y),
					new Point(start.Bounds.Right, conn.Level));
				path.AddLine(
					new Point(start.Bounds.Right, conn.Level),
					new Point(end.Bounds.Left, conn.Level));
				path.AddLine(
					new Point(end.Bounds.Left, conn.Level),
					new Point(end.Bounds.Left, WorkflowHelpers.GetCenter(end.Bounds).Y));
				newLineConnectors.Add(path);
			}
		}

		private void PaintFreeLoopConnectors(ActivityDesignerPaintEventArgs e)
		{
			GetWhileConnectors();
			for ( int i = 0; i < whileConnectors.Count; i++ ) {
				GraphicsPath path = whileConnectors[i];
				Point[] pts = Array.ConvertAll<PointF, Point>(path.PathPoints, WorkflowHelpers.ToPoint);
				using ( Pen connPen = new Pen(Color.Gray, e.DesignerTheme.ForegroundPen.Width) ) {
					DrawConnectors(e.Graphics, connPen, pts,
						 LineAnchor.None, LineAnchor.ArrowAnchor);
				}
				Activity source = Workflow.GetActivityByName(Workflow.WhileConnectors[i].Start);
                Point textLoc = Point.Empty;
                if ( path.PathPoints[1].Y < WorkflowHelpers.GetCenter(Bounds).Y + TitleHeight )
                    textLoc = new Point(pts[1].X + 10, pts[1].Y + 5);
                else
                    textLoc = new Point(pts[1].X + 10, pts[1].Y - 15);

                StringFormat format = new StringFormat();
                format.FormatFlags = StringFormatFlags.NoWrap;
                format.Trimming = StringTrimming.EllipsisCharacter;
                e.Graphics.DrawString(source.Name, e.DesignerTheme.Font,
                                      e.DesignerTheme.ForegroundBrush,
                                         new RectangleF(
                                             textLoc,
                                             new SizeF(95, 19)), format);
			}
		}

		private void PaintNewLineConnectors(ActivityDesignerPaintEventArgs e)
		{
			GetNewLineConnectors();
			foreach ( GraphicsPath path in newLineConnectors ) {
				Point[] pts = Array.ConvertAll<PointF, Point>(path.PathPoints, WorkflowHelpers.ToPoint);
				DrawConnectors(e.Graphics, e.DesignerTheme.ForegroundPen, pts, LineAnchor.None, LineAnchor.None);
			}
		}

		private Point PointFToPoint(PointF p)
		{
			return Point.Round(p);
		}

		private void PaintDocumentFlowConnectors(ActivityDesignerPaintEventArgs e)
		{
			//foreach ( DocumentFlowConnector conn in Workflow.DocumentFlows ) {

			//    if ( conn.Start == "startDoc" ) {
			//        doStartFlow(conn, e);
			//        continue;
			//    }

			//    Activity source = Workflow.GetActivityByName(conn.Start);
			//    Activity target = Workflow.GetActivityByName(conn.End);

			//    if ( source == null || target == null )
			//        continue;

			//    ActivityDesigner start = (ActivityDesigner)designerHost.GetDesigner(source);
			//    ActivityDesigner end = (ActivityDesigner)designerHost.GetDesigner(target);

			//    Point startCenter = new Point(WorkflowHelpers.GetCenter(start.Bounds).X, WorkflowHelpers.GetCenter(start.Bounds).Y + TitleHeight);
			//    Point endCenter = new Point(WorkflowHelpers.GetCenter(end.Bounds).X, WorkflowHelpers.GetCenter(end.Bounds).Y + TitleHeight);


			//    //Point firstTop;
			//    //Point secondTop;

			//    //if ( start.ParentDesigner is HorizontalWorkflowDesigner ) {
			//    //    firstTop =
			//    //        new Point((int)( start.Location.X + ( end.Location.X - start.Location.X ) * 0.33 )
			//    //        , start.ParentDesigner.Location.Y + 100);
			//    //    secondTop =
			//    //        new Point((int)( start.Location.X + ( end.Location.X - start.Location.X ) * 0.77 )
			//    //            , start.ParentDesigner.Location.Y + 100);
			//    //} else {
			//    //    firstTop =
			//    //        new Point((int)( start.Location.X + ( end.Location.X - start.Location.X ) * 0.33 )
			//    //        , start.ParentDesigner.Location.Y);
			//    //    secondTop =
			//    //        new Point((int)( start.Location.X + ( end.Location.X - start.Location.X ) * 0.77 )
			//    //            , start.ParentDesigner.Location.Y);
			//    //}

			//    int topX = startCenter.X + ( endCenter.X - startCenter.X ) / 2;
			//    if ( start.ParentDesigner is HorizontalWorkflowDesigner )
			//        topX += 100;


			//    if ( start.Location.Y <= end.Location.Y ) {
			//        e.Graphics.DrawCurve(_dottedPen,
			//            new Point[]
			//            {
			//                new Point(startCenter.X, start.Bounds.Top),
			//                new Point(topX, start.ParentDesigner.Bounds.Top),
			//                new Point(endCenter.X, end.Bounds.Top)
			//            });
			//    } else {
			//        e.Graphics.DrawCurve(_dottedPen,
			//            new Point[]
			//            {
			//                new Point(startCenter.X, start.Bounds.Bottom),
			//                new Point(topX, start.ParentDesigner.Bounds.Bottom),
			//                new Point(endCenter.X, end.Bounds.Bottom)
			//        });
			//    }
			//}
		}

		//private void createDocumentPath()
		//{
		//    _documentPath = new GraphicsPath();
		//    int cY = WorkflowHelpers.GetCenter(Bounds).Y + TitleHeight;
		//    // |
		//    _documentPath.AddLine(
		//        new Point(Bounds.X + 5, cY - 25), new Point(Bounds.X + 5, cY - 55));
		//    // -
		//    _documentPath.AddLine(
		//        new Point(Bounds.X + 5, cY - 55), new Point(Bounds.X + 20, cY - 55));
		//    //  \
		//    _documentPath.AddLine(
		//        new Point(Bounds.X + 20, cY - 55), new Point(Bounds.X + 25, cY - 50));
		//    //  |
		//    _documentPath.AddLine(
		//        new Point(Bounds.X + 25, cY - 50), new Point(Bounds.X + 25, cY - 25));
		//    _documentPath.CloseFigure();
		//    //// _
		//    //_documentPath.AddLine(
		//    //    new Point(Bounds.X + 30, cY + 10), new Point(Bounds.X + 10, cY + 10));
		//}

		private void createActivityVerbs()
		{
			try {
				this.verbs = new ActivityDesignerVerbCollection();
				this.verbs.Clear();

				this.verbs.Add(new ActivityDesignerVerb(this, DesignerVerbGroup.Actions, "Add New Line",
					new EventHandler(onAddBranch)));
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Source + " has encountered an error at " + ex.TargetSite + ":" + Environment.NewLine + Environment.NewLine +
					ex.Message, "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void onAddBranch(object sender, EventArgs e)
		{
			try {
				_branchList.Clear();
				AddBranches(1);
				ReadOnlyCollection<Activity> roc = _branchList.AsReadOnly();
				InsertActivities(HitTest(Location), roc);
				Workflow.NewLineConnectors.Add(
					new FreeConnector(Workflow.Activities[Workflow.Activities.Count - 2].Name, roc[0].Name));
                if(this.ContainedDesigners.Count< 2) return;
                CompositeActivityDesigner lastNewLine = this.ContainedDesigners[this.ContainedDesigners.Count - 2] as CompositeActivityDesigner;
                if ( lastNewLine.ContainedDesigners.Count < 1 ) return;
                ActivityDesigner lastDesigner = lastNewLine.ContainedDesigners[lastNewLine.ContainedDesigners.Count - 1];
                if ( lastDesigner.Activity is LogicalMethodCallActivity &&
                    lastDesigner.Activity.Name.EndsWith("Reply") ) {
                    List<Activity> list = new List<Activity>();
                    list.Add(lastDesigner.Activity);
                    lastNewLine = this.ContainedDesigners[this.ContainedDesigners.Count - 1] as CompositeActivityDesigner;
                    HitTestInfo hti = new HitTestInfo(lastDesigner, HitTestLocations.Designer);
                    lastNewLine.MoveActivities(hti, list.AsReadOnly());
                }
			} catch ( Exception ex ) {
				MessageBox.Show(ex.Source + " has encountered an error at " + ex.TargetSite + ":" + Environment.NewLine + Environment.NewLine +
					ex.Message, "Unhandled Exception", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		#endregion

		#region Public Members

		public bool IsDraggingLoopConnecotr = false;
		public Activity DraggedActivity;

		private List<FreeConnector> _connectorList = new List<FreeConnector>();
		public List<FreeConnector> ConnectorList
		{
			get { return _connectorList; }
		}

		private List<DocumentFlowConnector> _documentflow = new List<DocumentFlowConnector>();

		//private string _newActivityName;
		//private string _oldActivityName;

		public HorizontalWorkflowActivity Workflow
		{
			get { return this.Activity as HorizontalWorkflowActivity; }
		}

		public override Size MinimumSize
		{
			get { return new Size(300, 100); }
		}

		protected override void OnContainedActivityChanged(ActivityChangedEventArgs e)
		{
			if ( e.Member != null ) {
				if ( e.Member.Name == "Name" ) {
					foreach ( FreeConnector flc in Workflow.WhileConnectors ) {
						if ( flc.Start == e.OldValue.ToString() )
							flc.Start = e.NewValue.ToString();
						if ( flc.End == e.OldValue.ToString() )
							flc.End = e.NewValue.ToString();
					}
				}
			}
			base.OnContainedActivityChanged(e);

		}

		#endregion

		#region Protected Members

		public override ReadOnlyCollection<DesignerView> Views
		{
			get
			{
				if ( _views == null ) {
					_views = new List<DesignerView>();
					_views.Add(new HorizontalView(1,
						"View Horizontal Workflow",
						Resources.complex,
						this));
					_views.Add(new HorizontalView(2,
						"View Cancellation Handler",
						Resources.complex,
						this,
						typeof(HorizontalCancellationHandlerActivity)));
					_views.Add(new HorizontalView(3,
						"View Compensation Handler",
						Resources.complex,
						this,
						typeof(HorizontalCompensationHandlerActivity)));
					_views.Add(new HorizontalView(3,
						"View Fault Handler",
						Resources.complex,
						this,
						typeof(HorizontalFaultHandlersActivity)));
				}
				return _views.AsReadOnly();
			}
		}

		protected override void PostFilterProperties(System.Collections.IDictionary properties)
		{
			base.PostFilterProperties(properties);
		}

		protected override void PostFilterAttributes(System.Collections.IDictionary attributes)
		{
			base.PostFilterAttributes(attributes);
		}

		protected override void OnViewChanged(DesignerView view)
		{
			if ( this == this.ActiveView.AssociatedDesigner ) {
				verbs[0].Visible = true;
			} else {
				verbs[0].Visible = false;
			}
			base.OnViewChanged(view);
		}

        /// <summary>
        /// Draws the visual representation of activity at design time.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Workflow.ComponentModel.Design.ActivityDesignerPaintEventArgs"/> that contains the drawing arguments.</param>
		protected override void OnPaint(ActivityDesignerPaintEventArgs e)
		{
			this.Workflow.Activities.ToString();
			if ( !this.IsVisible )
				return;

			if ( designerHost == null )
				designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));
			if ( workflowView == null ) {
				workflowView = (WorkflowView)designerHost.GetService(typeof(WorkflowView));
				workflowView.MouseDown += new MouseEventHandler(workflowView_MouseDown);
				workflowView.MouseMove += new MouseEventHandler(workflowView_MouseMove);
				workflowView.MouseUp += new MouseEventHandler(workflowView_MouseUp);
			}

			if ( this == ActiveView.AssociatedDesigner ) {
				PaintFreeLoopConnectors(e);
				PaintNewLineConnectors(e);

				if ( selectedPath >= 0 && _selectionType != SelectionType.None ) {
					GraphicsPath selected = new GraphicsPath();
					if ( _selectionType == SelectionType.Jump ) {
						selected = whileConnectors[selectedPath];
					} else if ( _selectionType == SelectionType.NewLine ) {
						selected = newLineConnectors[selectedPath];
					}
					e.Graphics.DrawPath(_outLinePen, selected);
				}

				e.ClipRectangle.Inflate(30, 30);
				e.Graphics.ClipBounds.Inflate(30F, 30F);

				terminators = GetInnerConnections(DesignerEdges.None);
				// paint start
				Rectangle startTerminator = //new Rectangle(terminators[0].X, terminators[0].Y - 10, terminatorSize, terminatorSize);
					new Rectangle(Bounds.Left, WorkflowHelpers.GetCenter(ContainedDesigners[0].Bounds).Y - terminatorSize / 2,
						terminatorSize, terminatorSize);
				if ( e.Graphics.IsVisible(startTerminator) ) {
					e.Graphics.FillEllipse(Brushes.White, startTerminator);
					e.Graphics.DrawEllipse(e.DesignerTheme.ForegroundPen, startTerminator);
					drawBPMNElement(e.Graphics, e.DesignerTheme, startTerminator, EventType.None);
				}
				// paint end
				Rectangle endTerminator = //new Rectangle(terminators[1].X, terminators[1].Y - terminatorSize / 2, terminatorSize, terminatorSize);
					new Rectangle(ContainedDesigners[ContainedDesigners.Count - 1].Bounds.Right, WorkflowHelpers.GetCenter(ContainedDesigners[ContainedDesigners.Count - 1].Bounds).Y - terminatorSize / 2,
						terminatorSize, terminatorSize);
				if ( e.Graphics.IsVisible(endTerminator) ) {
					e.Graphics.FillEllipse(Brushes.White, endTerminator);
					e.Graphics.DrawEllipse(new Pen(e.DesignerTheme.ForegroundBrush, 2.0F), endTerminator);
					drawBPMNElement(e.Graphics, e.DesignerTheme, endTerminator, EventType.None);
				}
			}
			Rectangle header = new Rectangle(Location, new Size(Size.Width, 40));
			if ( _headerBrush == null )
				_headerBrush = new LinearGradientBrush(header, Color.SlateGray, Color.White, 270);
			Point headerText = new Point(Location.X + Size.Width / 2 - 50, Location.Y + 11);
			if ( e.Graphics.IsVisible(header) ) {
				e.Graphics.FillRectangle(_headerBrush, header);
				ActivityDesignerPaint.DrawText(e.Graphics, _headerFont,
					this.Activity.QualifiedName,
					header,
					StringAlignment.Center,
					TextQuality.Aliased,
					Brushes.Black);
			}

			base.PaintContainedDesigners(e);

			// Debug Rectangles
			// drop targets
			//e.Graphics.DrawRectangles(e.DesignerTheme.ForegroundPen, GetDropTargets(Bounds.Location));
		}

        /// <summary>
        /// Initializes the designer by using the specified activity.
        /// </summary>
        /// <param name="activity">The <see cref="T:System.Workflow.ComponentModel.Activity"/> with which to initialize the <see cref="T:System.Workflow.ComponentModel.Design.SequentialActivityDesigner"/>.</param>
		protected override void Initialize(Activity activity)
		{
			base.Initialize(activity);
			PerformLayout();
		}

        /// <summary>
        /// Performs layout tasks to position visual cues and child designers on the <see cref="T:System.Workflow.ComponentModel.Design.SequentialActivityDesigner"/>.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Workflow.ComponentModel.Design.ActivityDesignerLayoutEventArgs"/> that contains the layout arguments.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// 	<paramref name="e"/> contains a null reference (Nothing in Visual Basic).</exception>
		protected override void OnLayoutPosition(ActivityDesignerLayoutEventArgs e)
		{
			base.OnLayoutPosition(e);
			if ( this.Activity.QualifiedName != this.Activity.GetType().Name ) {
				if ( ContainedDesigners.Count == 0 ) {
					_branchList.Clear();
					AddBranches(1);
					InsertActivities(HitTest(Location), _branchList.AsReadOnly());
				}
			}
			int horizontalDistance = 50;
			foreach ( ActivityDesigner designer in ContainedDesigners ) {
				HorizontalSequenceDesigner branch = designer as HorizontalSequenceDesigner;
				if ( branch == null ) continue;
				int y = TitleHeight + horizontalDistance;
				branch.Location = new Point(Bounds.Left + terminatorSize, y);
				horizontalDistance += branch.Bounds.Height + 50;
			}
		}

        /// <summary>
        /// Sets the size of the visual cues on the designers contained within the <see cref="T:System.Workflow.ComponentModel.Design.SequentialActivityDesigner"/>.
        /// </summary>
        /// <param name="e">The <see cref="T:System.Workflow.ComponentModel.Design.ActivityDesignerLayoutEventArgs"/> that contains the layout arguments.</param>
        /// <returns>
        /// The <see cref="T:System.Drawing.Size"/> that contains the new size settings for <see cref="T:System.Workflow.ComponentModel.Design.SequentialActivityDesigner"/>.
        /// </returns>
		protected override Size OnLayoutSize(ActivityDesignerLayoutEventArgs e)
		{ 
			Size size = base.OnLayoutSize(e);
			if ( this.Activity.QualifiedName != this.Activity.GetType().Name ) {
				size.Width += ( width * 2 ) + 10;
				if ( Expanded )
					size.Height += 15;

				if ( ParentDesigner == null ) {
					//assign width of the longest branch to all branches
					int w = MinimumSize.Width;
					int h = MinimumSize.Height;
					foreach ( ActivityDesigner designer in ContainedDesigners ) {
						if ( designer.Size.Width > w ) {
							w = designer.Size.Width;
						}
						h += designer.Size.Height + 50;
					}

					foreach ( ActivityDesigner designer in ContainedDesigners ) {
						designer.Size = new Size(w, designer.Size.Height);
					}
					size.Width = w + ( terminatorSize * 2 ) + 10;
					size.Height = h + TitleHeight;
				}
				//size.Width -= 30;
                // catch connectors that are out the designer area
                foreach ( FreeConnector conn in this.Workflow.WhileConnectors ) {
                    if ( conn.Level > size.Height + 20 ) {
                        conn.Level = size.Height + 20;
                    } else if ( conn.Level < 75 ) {
                        conn.Level = 75;
                    }
                }
			}
			return size;
		}

		public override bool CanInsertActivities(HitTestInfo insertLocation, ReadOnlyCollection<Activity> activitiesToInsert)
		{
			if ( activitiesToInsert.Count == 1 &&
				activitiesToInsert[0] is HorizontalSequenceActivity ) return true;
			else return false;
		}

		protected override ReadOnlyCollection<Point> GetInnerConnections(DesignerEdges edges)
		{
			_innerConnections.Clear();

			_innerConnections.Add(new Point(Bounds.Left, WorkflowHelpers.GetCenter(Bounds).Y + TitleHeight));
			foreach ( ActivityDesigner designer in ContainedDesigners ) {
				if ( ( edges & DesignerEdges.Left ) == DesignerEdges.Left )
					_innerConnections.Add(new Point(designer.Bounds.Left, designer.Bounds.Y + designer.Bounds.Height / 2));
				if ( ( edges & DesignerEdges.Right ) == DesignerEdges.Right )
					_innerConnections.Add(new Point(designer.Bounds.Right, designer.Bounds.Y + designer.Bounds.Height / 2));
				if ( ( edges & DesignerEdges.Top ) == DesignerEdges.Top )
					_innerConnections.Add(new Point(designer.Bounds.X + designer.Bounds.Width / 2, designer.Bounds.Top));
				if ( ( edges & DesignerEdges.Bottom ) == DesignerEdges.Bottom )
					_innerConnections.Add(new Point(designer.Bounds.X + designer.Bounds.Width / 2, designer.Bounds.Bottom));
			}
			_innerConnections.Add(new Point(Bounds.Right - terminatorSize - 1, WorkflowHelpers.GetCenter(Bounds).Y + TitleHeight));

			return _innerConnections.AsReadOnly();
		}

		public override void RemoveActivities(ReadOnlyCollection<Activity> activitiesToRemove)
		{
			//if ( activitiesToRemove.Count == 1 ) {
			//    if ( activitiesToRemove[0] is HorizontalSequenceActivity ) {
			//        foreach ( FreeConnector conn in Workflow.NewLineConnectors ) {

			//        }
			//    }
			//}
			base.RemoveActivities(activitiesToRemove);
		}

		private void AddBranches(int count)
		{
			for ( int i = 0; i < count; i++ ) {
				_branchList.Add(new HorizontalSequenceActivity());
			}

			PerformLayout();
		}

		#region Drag & Drop
		protected override void OnDragLeave()
		{
			base.OnDragLeave();
			//_isDragging = false;
			Invalidate();
		}

		protected override void OnDragOver(ActivityDragEventArgs e)
		{
			base.OnDragOver(e);
			//_isDragging = true;
			_dragPosition = new Point(e.X, e.Y);

			Invalidate();
		}

		protected override void OnDragDrop(ActivityDragEventArgs e)
		{
			base.OnDragDrop(e);
			//_isDragging = false;
			Invalidate();
		}
		#endregion

		protected override void OnMouseMove(MouseEventArgs e)
		{

			base.OnMouseMove(e);
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			if ( e.KeyCode == Keys.Delete && selectedPath >= 0 ) {
				whileConnectors.RemoveAt(selectedPath);
				Workflow.WhileConnectors.RemoveAt(selectedPath);
				selectedPath = -1;
				Invalidate();
			} else {
				base.OnKeyDown(e);
			}
		}

		protected override int TitleHeight
		{
			get { return 40; }
		}

		protected override Rectangle SmartTagRectangle
		{
			get { return new Rectangle(); }
		}

		protected override void Dispose(bool disposing)
		{
			// graphics
			if ( _headerBrush != null )
				_headerBrush.Dispose();
			if ( _documentPath != null )
				_documentPath.Dispose();
			_headerFont.Dispose();
			_outLinePen.Dispose();

			// other
			whileConnectors.Clear();
			base.Dispose(disposing);
		}

		protected override ActivityDesignerVerbCollection Verbs
		{
			get
			{
				ActivityDesignerVerbCollection baseverbs = base.Verbs;
				try {
					if ( this.verbs == null ) {
						createActivityVerbs();
					}

					if ( !baseverbs.Contains(this.verbs[0]) ) {
						baseverbs.AddRange(this.verbs);
					}
				} catch ( Exception ex ) {
					MessageBox.Show(string.Format("{0} has encountered an error at {1}:{2}{2}{3}", ex.Source, ex.TargetSite, Environment.NewLine, ex.Message),
						"Unhandled Exception");
				}

				return baseverbs;
			}
		}

		protected override ActivityDesignerGlyphCollection Glyphs
		{
			get
			{
				return base.Glyphs;
				//if ( _glyphs == null ) {
				//    _glyphs = new ActivityDesignerGlyphCollection();
				//    _glyphs.Add(new CompensationGlyph(new EventHandler(handleGlyphClick)));
				//    _glyphs.Add(new ExceptionGlyph(new EventHandler(handleGlyphClick)));
				//}

				//ActivityDesignerGlyphCollection myGlyphs = base.Glyphs;
				//if ( this.Workflow.Activities.Find(FindCancellationHandler) != null ) {
				//    if ( !( myGlyphs.Contains(_glyphs[0]) ) ) {
				//        myGlyphs.Add(_glyphs[0]);
				//    }
				//} else {
				//    if ( myGlyphs.Contains(_glyphs[0]) ) {
				//        myGlyphs.Remove(_glyphs[0]);
				//    }
				//}
				//if ( this.Workflow.Activities.Find(FindFaultHandler) != null ) {
				//    if ( !( myGlyphs.Contains(_glyphs[1]) ) ) {
				//        myGlyphs.Add(_glyphs[1]);
				//    }
				//} else {
				//    if ( myGlyphs.Contains(_glyphs[1]) ) {
				//        myGlyphs.Remove(_glyphs[1]);
				//    }
				//}
				//return myGlyphs;
				////return base.Glyphs;
			}
		}

		#endregion

		// sender is DesignerGlyph
		// e is EventArgs.Empty
		private void handleGlyphClick(object sender, EventArgs e)
		{
			if ( sender is CompensationGlyph ) {
				if ( !( this.ActiveView.AssociatedDesigner is HorizontalCancellationHandlerDesigner ) ) {
					this.ActiveView = _views[1];
				} else {
					this.ActiveView = _views[0];
				}
			} else if ( sender is ExceptionGlyph ) {
				if ( !( this.ActiveView.AssociatedDesigner is HorizontalFaultHandlersDesigner ) ) {
					this.ActiveView = _views[3];
				} else {
					this.ActiveView = _views[0];
				}
			}
		}

		private bool FindCancellationHandler(Activity activity)
		{
			if ( activity is HorizontalCancellationHandlerActivity ) return true;
			else return false;
		}

		private bool FindFaultHandler(Activity activity)
		{
			if ( activity is HorizontalFaultHandlersActivity ) return true;
			else return false;
		}
	}

	public class HorizontalWorkflowActivityValidator : ActivityValidator
	{
		public override ValidationErrorCollection Validate(ValidationManager manager, object obj)
		{
			return new ValidationErrorCollection();
		}
	}

	public class HorizontalWorkflowTheme : CompositeDesignerTheme
	{
		public HorizontalWorkflowTheme(WorkflowTheme theme)
			: base(theme)
		{
		}
	}

	internal enum SelectionType
	{
		None = 0,
		Jump,
		NewLine
	}
}
